package comtuyrt7.libnavcompiler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.auto.service.AutoService;
import com.tuyrt7.libnavanotation.ActivityDestination;
import com.tuyrt7.libnavanotation.FragmentDestination;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

/**
 * @author: tuyrt7 on 2020/9/3.
 */

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes(value = {"com.tuyrt7.libnavanotation.ActivityDestination", "com.tuyrt7.libnavanotation.FragmentDestination"})
public class NavProcessor extends AbstractProcessor {

    private Messager mMessager;
    //写入文件
    private Filer mFiler;
    private static final String OUTPUT_FILE_NAME = "destination.json";

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mMessager = processingEnv.getMessager();
        mFiler = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> activityElements = roundEnv.getElementsAnnotatedWith(ActivityDestination.class);
        Set<? extends Element> fragmentElements = roundEnv.getElementsAnnotatedWith(FragmentDestination.class);

        if (!activityElements.isEmpty() || !fragmentElements.isEmpty()) {

            HashMap<String, JSONObject> destMap = new HashMap<>();
            handleDestination(activityElements, ActivityDestination.class, destMap);
            handleDestination(fragmentElements, FragmentDestination.class, destMap);

            //把 destMap 包装成jsonObject写入 assets/destination.json 文件中
            FileOutputStream fos = null;
            OutputStreamWriter writer = null;

            try {
                FileObject resource = mFiler.createResource(StandardLocation.CLASS_OUTPUT, "", OUTPUT_FILE_NAME);
                String resourcePath = resource.toUri().getPath();

                mMessager.printMessage(Diagnostic.Kind.NOTE, "resourcePath: " + resourcePath);
                // C:/Users/tuyrt/Desktop/own/jetpack/JetpackDemo/navigation/build/intermediates/javac/debug/classes/destination.json

                // appPath = ".../app/"
                String appPath = resourcePath.substring(0, resourcePath.indexOf("navigation") + "navigation".length()+1);
                //app/main/assets
                String assetsPath = appPath + "src/main/assets";
                File assetsDir = new File(assetsPath);
                if (!assetsDir.exists()) {
                    assetsDir.mkdirs();
                }

                File out = new File(assetsDir, OUTPUT_FILE_NAME);
                if (out.exists()) {
                    out.delete();
                }
                out.createNewFile();
                String content = JSON.toJSONString(destMap);

                fos = new FileOutputStream(out);
                writer = new OutputStreamWriter(fos, "utf-8");
                writer.write(content);
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return true;
    }

    private void handleDestination(Set<? extends Element> elements, Class<? extends Annotation> destinationClass, HashMap<String, JSONObject> destMap) {
        for (Element element : elements) {
            TypeElement typeElement = (TypeElement) element;
            String clazzName = typeElement.getQualifiedName().toString();
            int id = Math.abs(clazzName.hashCode());
            String pathUrl = "";
            boolean isFragment = false;
            boolean asStarter = false;
            boolean needLogin = false;

            Annotation annotation = typeElement.getAnnotation(destinationClass);
            if (annotation instanceof ActivityDestination) {
                ActivityDestination dest = (ActivityDestination) annotation;
                pathUrl = dest.pageUrl();
                asStarter = dest.asStarter();
                needLogin = dest.needLogin();
                isFragment = false;
            } else if (annotation instanceof FragmentDestination) {
                FragmentDestination dest = (FragmentDestination) annotation;
                pathUrl = dest.pageUrl();
                asStarter = dest.asStarter();
                needLogin = dest.needLogin();
                isFragment = true;
            }

            if (destMap.containsKey(pathUrl)) {
                mMessager.printMessage(Diagnostic.Kind.ERROR, "不同的页面不允许使用相同的pageUrl:" + pathUrl);
            } else {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("clazzName", clazzName);
                jsonObject.put("id", id);
                jsonObject.put("pathUrl", pathUrl);
                jsonObject.put("asStarter", asStarter);
                jsonObject.put("needLogin", needLogin);
                jsonObject.put("isFragment", isFragment);

                destMap.put(pathUrl, jsonObject);
            }

        }
    }
}
